package com.libre.workflow.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.libre.auth.entity.User;
import com.libre.auth.service.UserService;
import com.libre.common.exception.BusinessRuntimeException;
import com.libre.common.utils.DateTimeUtil;
import com.libre.common.utils.StringUtil;
import com.libre.common.utils.VerifyUtil;
import com.libre.common.vo.PageVo;
import com.libre.workflow.service.MyInstanceService;
import com.libre.workflow.vo.MyInstanceAddVo;
import com.libre.workflow.vo.MyInstanceStateVo;
import com.libre.workflow.vo.MyInstanceVo;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.runtime.ProcessInstanceBuilder;
import org.flowable.engine.runtime.ProcessInstanceQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * @author 庞留杰
 * @version V1.0
 * @description 流程实例
 * @date 2020/9/14 9:38
 **/
@Slf4j
@Service
@Transactional
public class MyInstanceServiceImpl implements MyInstanceService {

    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    RepositoryService repositoryService;
    @Autowired
    private UserService userService;

    /**
     * 功能描述: 启动：提交一个申请单
     *   启动流程实例(提交一个申请单)，实例启动成功，返回当前活动任务
     * @author: 庞留杰
     * @exception
     * @date: 2020/9/11 9:17
     */
    @Override
    public String startByKey(User user, MyInstanceAddVo instanceAddVo) {
        VerifyUtil.notEmpty("业务KEY", instanceAddVo.getBusinessKey());
        int paramsSet = ((instanceAddVo.getProcessDefinitionId() != null) ? 1 : 0) + ((instanceAddVo.getProcessDefinitionKey() != null) ? 1 : 0);
        if (paramsSet > 1) {
            throw new BusinessRuntimeException("processDefinitionId、processDefinitionKey或message中只能设置一个");
        }
        // 获取流程构造器
        ProcessInstanceBuilder processInstanceBuilder = runtimeService.createProcessInstanceBuilder();
        if (StringUtil.isNotEmpty(instanceAddVo.getProcessDefinitionId())) {
            processInstanceBuilder.processDefinitionId(instanceAddVo.getProcessDefinitionId());
        }
        if (StringUtil.isNotEmpty(instanceAddVo.getProcessDefinitionKey())) {
            processInstanceBuilder.processDefinitionKey(instanceAddVo.getProcessDefinitionKey());
        }
        if (StringUtil.isNotEmpty(instanceAddVo.getBusinessKey())) {
            processInstanceBuilder.businessKey(instanceAddVo.getBusinessKey());
        }
        if (StringUtil.isNotEmpty(instanceAddVo.getBusinessName())) {
            processInstanceBuilder.name(instanceAddVo.getBusinessName());
        }
        if (StringUtil.isNotEmpty(instanceAddVo.getTenantId())) {
            processInstanceBuilder.tenantId(instanceAddVo.getTenantId());
        }
        // 设置参数
        processInstanceBuilder.variables(instanceAddVo.getVariables());
        /**
         *  设置流程发起人
         * */
        //String user_id = user.getId();
        String user_id = "1";// TODO 这个判断后期删除
        log.info(" >>>>>>>>>>>>>>>>>> 流程发起人ID：{} >>>>>>>>>>>>>>>>> ", user_id);

        Authentication.setAuthenticatedUserId(user_id);
        ProcessInstance pi = null;
        try {
            // 启动（即创建）流程实例
            ProcessInstance processInstance = processInstanceBuilder.start();
            pi = processInstance;
            log.info("发起流程成功，流程ID:{}", processInstance.getId());
        } catch (FlowableObjectNotFoundException e) {
            throw new BusinessRuntimeException("启动流程失败，流程尚未发布成功");
        }
        //通过processInstance.getId() 可以判断流程是否发起成功
        return pi.getId();
    }


    /**
     * 功能描述: 分页查询流程实例【操作表：act_ru_execution】
     *
     * @param instanceVo
     * @throws
     * @author: 庞留杰
     * @return:
     * @date: 2020/9/14 9:44
     */
    @Override
    public Page findPage(PageVo pageVo, MyInstanceVo instanceVo) {
        int size = pageVo.getPageSize();
        int current = pageVo.getPageNumber();
        int firstResult = (current - 1) * size;// 第一条数据
        int maxResults = size;// 每页多少条
        ProcessInstanceQuery query = runtimeService.createProcessInstanceQuery();
        if (StringUtil.isNotEmpty(instanceVo.getBusinessKey())) {
            query.processInstanceBusinessKey(instanceVo.getBusinessKey());
        }
        if (StringUtil.isNotEmpty(instanceVo.getName())) {
            query.processInstanceNameLike("%" + instanceVo.getName() + "%");
        }
        long count = query.count();
        List<ProcessInstance> processInstanceList = query.orderByStartTime().desc().listPage(firstResult, maxResults);
        JSONArray arr = new JSONArray();
        //查询所有用户信息
        List<User> allUsers = userService.list();
        for (ProcessInstance processInstance : processInstanceList) {
            JSONObject obj = new JSONObject();
            //自能查出ID
            /**
             * 当流程中没有分支时，Execution等同于ProcessInstance，甚至连ID也相同；
             * 当流程中存在分支(fork, parallel gateway)，则在分支口会形成子Execution，在下一个gateway才会合并(joined)
             * */
            obj.put("executionId", processInstance.getId());//执行实例 Execution_ID
            obj.put("name", processInstance.getName());
            obj.put("businessKey", processInstance.getBusinessKey());
            obj.put("startTime", DateTimeUtil.format(processInstance.getStartTime(), DateTimeUtil.DATE_TIME));
            //发起人
            User user = allUsers.stream().filter(u -> (u.getId()).equals(processInstance.getStartUserId())).findAny().orElse(null);
            if (user != null) {
                obj.put("startUserId", processInstance.getStartUserId());//发起人ID
                obj.put("startUserName", user.getRealName());//发起人
            } else {
                obj.put("startUserId", processInstance.getStartUserId());//发起人ID
                obj.put("startUserName", null);//发起人
            }
            obj.put("processDefinitionId", processInstance.getProcessDefinitionId());//流程定义ID
            obj.put("processDefinitionKey", processInstance.getProcessDefinitionKey());
            obj.put("processDefinitionName", processInstance.getProcessDefinitionName());
            obj.put("description", processInstance.getDescription());//备注/描述
            obj.put("processInstanceId", processInstance.getProcessInstanceId());//流程实例ID
            //isSuspended 如果流程实例被挂起，则返回true  （1挂起，2激活） 【对应act_ru_execution：SUSPENSION_STATE_】
            obj.put("isSuspended", processInstance.isSuspended() == true ? 1 : 2);
            obj.put("isSuspendedCN", processInstance.isSuspended() == true ? "挂起" : "激活");
            //流程是否结束  【对应act_ru_execution：IS_CONCURRENT_】
            obj.put("isEnded", processInstance.isEnded());
            obj.put("isEndedCN", processInstance.isEnded() ? "结束" : "进行中");
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processInstance.getProcessDefinitionId()).singleResult();
            obj.put("imgName", processDefinition.getDiagramResourceName());
            obj.put("xmlName", processDefinition.getResourceName());
            arr.add(obj);
            log.info("执行实例ID：{}", processInstance.getId());
            log.info("流程定义ID：{}", processInstance.getProcessDefinitionId());
            log.info("流程实例ID：{}", processInstance.getProcessInstanceId());
        }
        Page page = new Page(current, size, count);
        page.setRecords(arr);
        page.setTotal(count);
        return page;
    }

    /**
     * 功能描述: 流程的挂起、激活
     * @author: 庞留杰
     * @param instanceStateVo
     * @return: Result
     * @exception
     * @date: 2020/9/14 9:53
     */
    @Override
    public String editState(MyInstanceStateVo instanceStateVo) {
        if ("1".equals(instanceStateVo.getAction())) {
            try {
                //挂起流程
                runtimeService.suspendProcessInstanceById(instanceStateVo.getProcessInstanceId());
            } catch (FlowableObjectNotFoundException e) {
                throw new BusinessRuntimeException("通过ProcessInstanceId：" + instanceStateVo.getProcessInstanceId() + "找不到流程实例");
            } catch (FlowableException e) {
                throw new BusinessRuntimeException("流程实例" + instanceStateVo.getProcessInstanceId() + "已经处于挂起状态");
            }
            return "挂起成功";
        } else {
            try {
                //激活流程
                runtimeService.activateProcessInstanceById(instanceStateVo.getProcessInstanceId());
            } catch (FlowableObjectNotFoundException e) {
                throw new BusinessRuntimeException("通过ProcessInstanceId：" + instanceStateVo.getProcessInstanceId() + "找不到流程实例");
            } catch (FlowableException e) {
                throw new BusinessRuntimeException("流程实例" + instanceStateVo.getProcessInstanceId() + "已经处于激活状态");
            }
            return "激活成功";
        }
    }

    /**
     * 删除流程
     * */
    @Override
    public void delete(String processInstanceId) {
        /**
         * Delete an existing runtime process instance.
         * 删除现有的运行时流程实例。
         * @param processInstanceId 要删除的流程实例的id不能为空。
         *     id of process instance to delete, cannot be null.
         * @param deleteReason  删除原因，可以为空。
         *     reason for deleting, can be null.
         * @throws FlowableObjectNotFoundException  当没有找到具有给定id的流程实例时。
         *     when no process instance is found with the given id.
         */
        try {
            runtimeService.deleteProcessInstance(processInstanceId, null);
        } catch (FlowableObjectNotFoundException e) {
            throw new BusinessRuntimeException("流程ID为：" + processInstanceId + "的流程不存在");
        }
    }

}
